#include "vc1dec_definitions.h"
#include "vc1stream.h"
#include "vc1dec_api.h"
#include "vc1decpic_sm.h"

#include "vc1mem.h"
#include "vc1vlc.h"
#include "vc1tables.h"

static U32_VC1 VC1CalculateMemory(I32_VC1 iWidth,I32_VC1 iHeight)
{
	U32_VC1 size = sizeof(sVC1Decoder);
	I32_VC1 w,h;
	I32_VC1 widthMB, heightMB;

	size = ((size + 3) >> 2) << 2;//4 bytes aligned

	w = ((iWidth + 15) >> 4) << 4;//microblock aligned
	h = ((iHeight + 15) >> 4) << 4;

	size += sizeof(sVC1Picture) * 3;

	size += (w * h * 3 / 2) * 3;/////total pixel memory of 3 frames

	widthMB = (iWidth + 15) >> 4;
	heightMB = (iHeight + 15) >> 4;

	size += (((widthMB * heightMB + 3) >> 2) << 2) * 2;/////bitplane size 4 bytes aligned

	size += ((widthMB * heightMB * sizeof(sVC1MB) + 3) >> 2) << 2;////all mb memory

	return size;

}

static HRESULT_VC1 InitVLCTables(sVC1Decoder* pDec)
{
	HRESULT_VC1 rt = VC1_OK;
	if(VC1ConvertVLCTable(&pDec->pICBPYTableSM,VC1ICBPYTableSM,VC1CBPVLCPEEKBITS) < 0)
		return VC1_UNKNOWERROR;
	return rt;
}

HRESULT_VC1 VC1Dec_Init(VC1DECODER* psDecoder,int iWidth,int iHeight,char profile,char level,unsigned char* pSequence,int iLength)
{
	HRESULT_VC1 rt = VC1_OK;
	U8_VC1 value = 0;
	sVC1Stream* pStream;
	sVC1Decoder* pDec = VC1_NULL;
	U32_VC1 totalSize = VC1CalculateMemory(iWidth,iHeight);
	U32_VC1 size = ((sizeof(sVC1Decoder) + 3) >> 2) << 2;
	I32_VC1 w,h;
	w = ((iWidth + 15) >> 4) << 4;//microblock aligned
	h = ((iHeight + 15) >> 4) << 4;


	pDec = (sVC1Decoder*)VC1Malloc(totalSize,4);
	if(!pDec)
		return VC1_INSUFFICIENT_MEMORY;

	pStream = &pDec->stream;

	if(STREAM_INVALIDBUFFER == VC1InitStream(pStream,pSequence,iLength))
		return VC1_INVALID_SEQUENCE;

	pDec->width = iWidth;
	pDec->height = iHeight;

	pDec->widthMBs = (iWidth + 15) >> 4;
	pDec->heightMBs = (iHeight + 15) >> 4;
	pDec->numMBs = pDec->widthMBs * pDec->heightMBs;

	value = VC1GetBits(pStream,4);///profile
	if(0 == value)
		pDec->profile = VC1_eProfileSimple;
	else if(4 == value)
		pDec->profile = VC1_eProfileMain;
	else if(8 == value)
		pDec->profile = VC1_eProfileAdvanced;
	else
		return VC1_INVALIDPROFILE;

	value = VC1GetBits(pStream,3);////
	pDec->frmrtq = value;

	value = VC1GetBits(pStream,5);
	pDec->bitrtq = value;

	value = VC1GetBits(pStream,1);
	pDec->loopfilter = value;

	value = VC1GetBits(pStream,1);///reserve

	value = VC1GetBits(pStream,1);
	pDec->multires = value;

	value = VC1GetBits(pStream,1);///reserve
	
	value = VC1GetBits(pStream,1);
	pDec->fastuvmc = value;

	value = VC1GetBits(pStream,1);
	pDec->ext_mv = value;

	value = VC1GetBits(pStream,2);
	pDec->dquant = value;

	value = VC1GetBits(pStream,1);
	pDec->vstransform = value;

	value = VC1GetBits(pStream,1);//reserve

	value = VC1GetBits(pStream,1);
	pDec->overlap = value;

	value = VC1GetBits(pStream,1);
	pDec->syncmarker = value;

	value = VC1GetBits(pStream,1);
	pDec->rangered = value;

	value = VC1GetBits(pStream,3);
	pDec->maxbframes = value;

	value = VC1GetBits(pStream,2);
	pDec->quantizer = value;

	value = VC1GetBits(pStream,1);
	pDec->finterpflag = value;

	value = VC1GetBits(pStream,1);//reserve






	pDec->pRef0Picture = (sVC1Picture*)((U8_VC1*)pDec + size);
	size += sizeof(sVC1Picture);
	pDec->pRef0Picture->Y = (U8_VC1*)pDec + size;
	size += w * h;
	pDec->pRef0Picture->U = (U8_VC1*)pDec + size;
	size += w * h / 4;
	pDec->pRef0Picture->V = (U8_VC1*)pDec + size;
	size += w * h / 4;

	pDec->pRef1Picture = (sVC1Picture*)((U8_VC1*)pDec + size);
	size += sizeof(sVC1Picture);
	pDec->pRef1Picture->Y = (U8_VC1*)pDec + size;
	size += w * h;
	pDec->pRef1Picture->U = (U8_VC1*)pDec + size;
	size += w * h / 4;
	pDec->pRef1Picture->V = (U8_VC1*)pDec + size;
	size += w * h / 4;

	pDec->pBPicture = (sVC1Picture*)((U8_VC1*)pDec + size);
	size += sizeof(sVC1Picture);
	pDec->pBPicture->Y = (U8_VC1*)pDec + size;
	size += w * h;
	pDec->pBPicture->U = (U8_VC1*)pDec + size;
	size += w * h / 4;
	pDec->pBPicture->V = (U8_VC1*)pDec + size;
	size += w * h / 4;

	pDec->pRef0Picture->stride = w;
	pDec->pRef0Picture->display = 0;
	pDec->pRef1Picture->stride = w;
	pDec->pRef1Picture->display = 0;
	pDec->pBPicture->stride = w;
	pDec->pBPicture->display = 0;

	pDec->pOutPicture = NULL;///no output
	pDec->pCurPicture = NULL;

	pDec->bitplane = (U8_VC1*)pDec + size;
	size += (((pDec->numMBs + 3) >> 2) << 2) * 2;

	pDec->pAllMBs = (sVC1MB*)((U8_VC1*)pDec + size);
	size += (((pDec->numMBs * sizeof(sVC1MB) + 3) >> 2) << 2);

	pDec->firstframe = 1;

	if((rt = InitVLCTables(pDec)) != VC1_OK)////failed to init vlc tables
		return rt;

	*psDecoder = (VC1DECODER)pDec;

	return rt;
}

HRESULT_VC1 VC1Dec_Close(VC1DECODER sDecoder)
{
	HRESULT_VC1 rt = VC1_OK;
	sVC1Decoder* pDec = (sVC1Decoder*)sDecoder;
	I32_VC1 i = 0;
	if(!pDec)
		return VC1_UNKNOWERROR;
	if(pDec->pICBPYTableSM)
		VC1Free(pDec->pICBPYTableSM);

	for(i = 0; i < 4; i++)
	{
		if(pDec->pPBCBPYTableSM[i])
			VC1Free(pDec->pPBCBPYTableSM[i]);
	}

	VC1Free(sDecoder);
	return rt;
}


//////////decoded order ----> display order
static HRESULT_VC1 VC1SetOutPicture(sVC1Decoder* pDec)
{
	HRESULT_VC1 rt = VC1_OK;
	sVC1Picture* out = NULL;

	pDec->pCurPicture->display++;////out is raedy to display

	if(pDec->firstframe == 1)
	{
		pDec->firstframe = 0;
		return VC1_FRAMENOTREADY;
	}

	if(pDec->ePictureType == VC1_PICTURE_SKIP || pDec->ePictureType == VC1_PICTURE_BI)
	{
		if(pDec->pRef1Picture->display <= 0)/////////something error
			return VC1_FRAMENOTREADY;
		out = pDec->pRef1Picture;
	}
	else///I P B
	{
		if(pDec->pBPicture->display > 0)
			out = pDec->pBPicture;
		else if(pDec->pBPicture->display > 0)
			out = pDec->pRef0Picture;
		else
			return VC1_FRAMENOTREADY;
	}
	pDec->pOutPicture = out;
	return rt;
	
}

static HRESULT_VC1 VC1AdjustRefPicAndIO(sVC1Decoder* pDec)
{
	VC1_ePictureType type = pDec->ePictureType;
	sVC1Picture* tmp = NULL;
	switch(type)
		{
		case VC1_PICTURE_I:
		case VC1_PICTURE_P:
			tmp = pDec->pRef0Picture;
			if(tmp->display > 0)
				return VC1_BUFFEROVERFLOW;
			pDec->pRef0Picture = pDec->pRef1Picture;
			pDec->pRef1Picture = tmp;
			pDec->pCurPicture = pDec->pRef1Picture;
			break;
		case VC1_PICTURE_BI:
		case VC1_PICTURE_B:
			/////don't swap ref picture pointer
			pDec->pCurPicture = pDec->pBPicture;
			break;
		case VC1_PICTURE_SKIP:
			pDec->pCurPicture = pDec->pRef1Picture;
			break;
		default:
			return VC1_UNKNOWNPICTURETYPE;
		}

	return VC1_OK;
}

HRESULT_VC1 VC1Dec_DecodeData(VC1DECODER sDecoder, unsigned char* pBuf, int iLen)
{
	HRESULT_VC1 rt = VC1_OK;
	sVC1Decoder* pDec = (sVC1Decoder*)sDecoder;
	sVC1Stream*  pStream = NULL;
	if(!pDec)
		return VC1_INVALIDHANDLE;

	if(!pBuf || iLen <= 0)
		return VC1_INVALIDBITSTREAM;

	pStream = &pDec->stream;
	if(VC1InitStream(pStream,pBuf,iLen) != STREAM_SUCCEEDED)
		return VC1_INVALIDBITSTREAM;
	
	if(pDec->profile < VC1_eProfileAdvanced)///Simple/Main Profile
	{
		if(iLen > 1)///not skip frame
		{
			HRESULT_VC1 result = VC1DecPicHeader_sm(pDec);
			if(result != VC1_OK)
				return result;
		}
		else
			pDec->ePictureType = VC1_PICTURE_SKIP;

		if((rt = VC1AdjustRefPicAndIO(pDec)) != VC1_OK)
			return rt;

		if(pDec->profile != VC1_PICTURE_SKIP)
		{
			
		}
		
		if((rt = VC1SetOutPicture(pDec)) != VC1_OK)
			return rt;

	}
	else if(VC1_eProfileAdvanced == pDec->profile)
	{
		////to be continued
		return VC1_PROFILENOTSUPPORTED;
	}
	else
		return VC1_PROFILENOTSUPPORTED;

	return rt;
}

HRESULT_VC1 VC1Dec_GetDecodedFrame(VC1DECODER sDecoder,unsigned char** pY,unsigned char** pU,unsigned char** pV,int* pWidth, int* pHeight, int* pStride)
{
	HRESULT_VC1 rt = VC1_OK;
	sVC1Picture* pOut;
	sVC1Decoder* pDec = (sVC1Decoder*)sDecoder;
	if(!pDec)
		return VC1_INVALIDHANDLE;

	pOut = pDec->pOutPicture;
	if(pOut == NULL)
		return VC1_UNKNOWERROR;

	*pY = pOut->Y;
	*pU = pOut->U;
	*pU = pOut->V;
	*pWidth = pDec->width;
	*pHeight = pDec->height;
	*pStride = pOut->stride;

	pDec->pOutPicture->display--;////
	pDec->pOutPicture = NULL;

	return rt;
}